Skip to content

Conversation

@ParaskP7
Copy link
Contributor

@ParaskP7 ParaskP7 commented Sep 8, 2025

Closes: AINFRA-554


Description

This PR migrates WCNewVisitorStatsModel from WellSQL to Room database.


Reviewers

In addition to having @wzieba as the main "code" reviewer on this change, I added @woocommerce/android-developers to randomly assign a product engineers as an "extra" review (@irfano @hichamboushaba), primarily because, both on trunk and this PR branch, I've these questions (⚠️):

  1. OrderStatsRestClient.fetchNewVisitorStats(...): This is where the only place where WCNewVisitorStatsModel get instantiated and its isCustomField is being driven by startDate != null, questions:
    • Why is startDate driving this isCustomField field? 🤔
    • Due to the above, during my testing, isCustomField was always 1, or else true, just because startDate was always non-null, no matter what option the user chooses from the UI (My store -> Performance -> Calendar icon). Is that expected? 🤔
  2. WCNewVisitorStatsModel: During my testing, when using Custom from the UI (My store -> Performance -> Calendar icon), I noticed that the Visitors (and Conversion) data is unavailable for any date range picked other than a specific "single" day. Is that expected? 🤔

Testing information

My store + Performance

  1. Navigate on the My store tab and verify that the Performance section is being correctly populated with the expected number of Visitors.
  2. On that Performance section, click on the Calendar icon and select Today. Verify that the section's title changes to something like Today Sep 9, 2025 and that the number of Visitors is updated accordingly.
  3. Similarly, select This Week, This Month and This Year and verify everything is working as expected.
  4. Open App Inspection, check the NewVisitorStatsEntity table, and notice (⚠️) that isCustomField is 1.
  5. Now, select Custom and edit the section's title picking a specific "single" day like Sep 9 - Sep 9, click SAVE. Verify that the section's title changes to something like Custom Sep 9, 2025 - Sep 9, 2025 and that the number of Visitors is updated accordingly.
  6. Then, edit the section's title again, picking any other range, except for a "single" day, like Sep 8 - Sep 9, click SAVE. Verify that the section's title changes to something like Custom Sep 8, 2025 - Sep 9, 2025 and notice (⚠️) that the number of Visitors (and Conversion) is actually unavailable. (see also Reviewers related questions).
  7. Open App Inspection, check the NewVisitorStatsEntity table, and verify that isCustomField is 1.

Images/gif

image
  • I have considered if this change warrants release notes and have added them to RELEASE-NOTES.txt if necessary. Use the "[Internal]" label for non-user-facing changes.

Warning: "'Enum.values()' is recommended to be replaced by
'Enum.entries' since 1.9"
Warning: "Call chain on a collection type may be simplified"
FYI: This is done in preparation to the subsequent WellSQL to Room
table migration.
FYI: This is done in preparation to the subsequent WellSQL to Room
table migration.
FYI: This is done in preparation to the subsequent WellSQL to Room
table migration.
FYI: This change includes but is not limited to the:
- Entity Introduction
- DAO Introduction (With Test)
- WCAndroidDatabase Migration
- Code Adjustments (Store)
- Test Adjustments (Store)
@ParaskP7 ParaskP7 added this to the 23.3 milestone Sep 8, 2025
@ParaskP7 ParaskP7 requested a review from Copilot September 8, 2025 16:03
@ParaskP7 ParaskP7 added the type: technical debt Represents or solves tech debt of the project. label Sep 8, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates WCNewVisitorStatsModel from WellSQL to Room database as part of the ongoing database migration effort. The change replaces WellSQL-based persistence with modern Room annotations and DAOs while maintaining the same functionality.

Key changes:

  • Migrates WCNewVisitorStatsModel from WellSQL annotations to Room entity
  • Replaces WCVisitorStatsSqlUtils with NewVisitorStatsDao using Room queries
  • Updates database schema and migration scripts

Reviewed Changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
WCNewVisitorStatsModel.kt Converted from WellSQL @Table to Room @Entity with composite primary key
NewVisitorStatsDao.kt New Room DAO replacing WellSQL utility functions
WCStatsStore.kt Updated to use new DAO instead of SQL utils, made functions suspend
WCVisitorStatsSqlUtils.kt Deleted - functionality moved to Room DAO
WCAndroidDatabase.kt Added new entity and DAO registration, version bump
WellSqlConfig.kt Added migration to drop old WellSQL table
Test files Updated to use new Room-based approach and test patterns

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

}
} ?: return mapOf()
}
return mapOf()
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return statement at line 213 is unreachable because the function always returns from within the rawStats?.let block or the ?: return mapOf() expression on line 212. Remove the unreachable return statement.

Suggested change
return mapOf()

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

@ParaskP7 ParaskP7 Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, and that's exactly what I did, actually a bit confused by this comment of yours... 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the comment is incorrect - I guess LLM wrongly interpreted the inner return (line 203).

A suggestion I have is to refactor for a single return point, but that's not critical.

val visitorMap = rawStats?.let { visitorStatsModel ->
        val periodIndex = visitorStatsModel.getIndexForField(WCNewVisitorStatsModel.VisitorStatsField.PERIOD)
        val fieldIndex = visitorStatsModel.getIndexForField(WCNewVisitorStatsModel.VisitorStatsField.VISITORS)

        if (periodIndex != -1 && fieldIndex != -1) {
            getVisitorsMap(
                periodIndex = periodIndex,
                fieldIndex = fieldIndex,
                dataList = visitorStatsModel.dataList
            )
        } else {
            emptyMap()
        }
    }

    return visitorMap ?: emptyMap()

@dangermattic
Copy link
Collaborator

dangermattic commented Sep 8, 2025

1 Warning
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Sep 8, 2025

📲 You can test the changes from this Pull Request in WooCommerce-Wear Android by scanning the QR code below to install the corresponding build.
App Name WooCommerce-Wear Android
Platform⌚️ Wear OS
FlavorJalapeno
Build TypeDebug
Commit2e11297
Direct Downloadwoocommerce-wear-prototype-build-pr14592-2e11297.apk

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Sep 8, 2025

📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App Name WooCommerce Android
Platform📱 Mobile
FlavorJalapeno
Build TypeDebug
Commit2e11297
Direct Downloadwoocommerce-prototype-build-pr14592-2e11297.apk

@codecov-commenter
Copy link

codecov-commenter commented Sep 8, 2025

Codecov Report

❌ Patch coverage is 6.49351% with 72 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.38%. Comparing base (92303be) to head (2e11297).

Files with missing lines Patch % Lines
.../org/wordpress/android/fluxc/store/WCStatsStore.kt 9.80% 46 Missing ⚠️
...ress/android/fluxc/model/WCNewVisitorStatsModel.kt 0.00% 9 Missing ⚠️
...k/rest/wpcom/wc/orderstats/OrderStatsRestClient.kt 0.00% 9 Missing ⚠️
...ndroid/fluxc/persistence/dao/NewVisitorStatsDao.kt 0.00% 4 Missing ⚠️
...rdpress/android/fluxc/persistence/WellSqlConfig.kt 0.00% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk   #14592      +/-   ##
============================================
+ Coverage     38.35%   38.38%   +0.02%     
  Complexity     9715     9715              
============================================
  Files          2058     2058              
  Lines        115287   115226      -61     
  Branches      15341    15337       -4     
============================================
+ Hits          44220    44224       +4     
+ Misses        66970    66906      -64     
+ Partials       4097     4096       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…to migrate/wc-new-visitor-stats-model-to-room
@ParaskP7 ParaskP7 marked this pull request as ready for review September 9, 2025 11:14
@ParaskP7 ParaskP7 requested review from a team, irfano and wzieba and removed request for a team September 9, 2025 11:14
@irfano irfano removed their request for review September 9, 2025 16:28
@irfano
Copy link
Contributor

irfano commented Sep 9, 2025

I unassigned myself. Since I’ll be AFK until September 19, can you take a look at this, @JorgeMucientes or @hichamboushaba?

@ParaskP7
Copy link
Contributor Author

I unassigned myself. Since I’ll be AFK until September 19...

Oh, thanks @irfano and apologies, I (or algorithm) wasn't aware you're on AFK. 🫣

FYI: I am not sure if you had the change to use this AFK/Github Busy Status Sync (paaHJt-8Ri-p2) yet, it might help you folks in situations like this in the future. Cc @woocommerce/android-developers for further awareness. 🙏

...can you take a look at this, @JorgeMucientes or @hichamboushaba?

Since I've assign @JorgeMucientes on this previous #14520 PR of mine already, I'll go ahead and assign you @hichamboushaba on this one, let me know how you feel and I'll find an alternative. 🙏

Copy link
Contributor

@wzieba wzieba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

I don't know the answers on the domain of the Stats, so I'll leave this to the product team. I also couldn't perform detailed tests due to log in issues (internal details: p1757495577372719-slack-CGPNUU63E ) but I don't want to keep this PR on hold because of this.

}
} ?: return mapOf()
}
return mapOf()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the comment is incorrect - I guess LLM wrongly interpreted the inner return (line 203).

A suggestion I have is to refactor for a single return point, but that's not critical.

val visitorMap = rawStats?.let { visitorStatsModel ->
        val periodIndex = visitorStatsModel.getIndexForField(WCNewVisitorStatsModel.VisitorStatsField.PERIOD)
        val fieldIndex = visitorStatsModel.getIndexForField(WCNewVisitorStatsModel.VisitorStatsField.VISITORS)

        if (periodIndex != -1 && fieldIndex != -1) {
            getVisitorsMap(
                periodIndex = periodIndex,
                fieldIndex = fieldIndex,
                dataList = visitorStatsModel.dataList
            )
        } else {
            emptyMap()
        }
    }

    return visitorMap ?: emptyMap()

<ID>UnitTestNamingRule:WCShippingLabelStoreTest.kt$WCShippingLabelStoreTest$@Test fun `verify shipping address`()</ID>
<ID>UnitTestNamingRule:WCStatsSqlUtilsTest.kt$WCStatsSqlUtilsTest$@Test @Suppress("LongMethod") fun testGetRawRevenueStatsForSiteAndUnit()</ID>
<ID>UnitTestNamingRule:WCStatsSqlUtilsTest.kt$WCStatsSqlUtilsTest$@Test @Suppress("LongMethod") fun testSimpleInsertionAndRetrievalOfRevenueStats()</ID>
<ID>UnitTestNamingRule:WCStatsStoreTest.kt$WCStatsStoreTest$@Suppress("LongMethod") @Test fun testGetQuantityForMonths()</ID>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on this (here and previous PRs) - a good reminder to look at the baseline (Detekt or Lint) when working on technical debt 🏅

@ParaskP7
Copy link
Contributor Author

Thanks for the review @wzieba (🙇) , I'll wait for another review from the product team, plus maybe you being able to successfully test this afterwards, and I'll then proceed with applying any of the review suggestions (like this one). 👍

@hichamboushaba
Copy link
Member

I'll go ahead and assign you @hichamboushaba on this one, let me know how you feel and I'll find an alternative. 🙏

Sounds good @ParaskP7, but I hope it's fine not to review this until tomorrow 🙂

@ParaskP7
Copy link
Contributor Author

Sounds good @ParaskP7, but I hope it's fine not to review this until tomorrow 🙂

Totally fine @hichamboushaba , many thanks + appreciated, and no rush, no rush at all! 🙇 ❤️

@hichamboushaba hichamboushaba self-assigned this Sep 11, 2025
Copy link
Member

@hichamboushaba hichamboushaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @ParaskP7 👏, this works as expected, and I'll try to answer your questions below:

OrderStatsRestClient.fetchNewVisitorStats(...): This is where the only place where WCNewVisitorStatsModel get instantiated and its isCustomField is being driven by startDate != null, questions:

  • Why is startDate driving this isCustomField field? 🤔
  • Due to the above, during my testing, isCustomField was always 1, or else true, just because startDate was always non-null, no matter what option the user chooses from the UI (My store -> Performance -> Calendar icon). Is that expected? 🤔

I also didn't know, but after looking at the history of this field, I understand now, back in 2019 when this table was added, the app supported a "custom" date option in stats graph (that option was removed later), and the way the team worked on this was that they treated the granularity option as a date (so StatsGranularity.DAYS was equivalent to "Today") unless the startDate was specified, startDate was specified only for the custom date option in the graph.
This was confusing, and we got rid of this when we brought back the "Custom" date option last year, and we changed the logic so that startDate was always required.
I overlooked the column isCustomField of the DB when I worked on this, otherwise I should have deleted it as it's always true now.
Now whether you should delete it or not, I'll leave it up to you to decide this 🙂.

WCNewVisitorStatsModel: During my testing, when using Custom from the UI (My store -> Performance -> Calendar icon), I noticed that the Visitors (and Conversion) data is unavailable for any date range picked other than a specific "single" day. Is that expected? 🤔

Yes, it's expected, but this has nothing to do with this migration, the Visitors count that's shown on the graph when no entry is selected is driven by the VisitorSummaryStatsEntity, and this used Room since it was added.


Please let me know if you have any other questions, but the main point here is that this is ready for merge unless you want to delete the isCustomField column.

@ParaskP7
Copy link
Contributor Author

👋 @hichamboushaba and thanks for reviewing/testing this, as well as answering my questions, much appreciated! 🙇 ❤️

Nice work @ParaskP7 👏, this works as expected, and I'll try to answer your questions below:

Thank you for digging as deep to answer my question! 🙏

Please let me know if you have any other questions, but the main point here is that this is ready for merge unless you want to delete the isCustomField column.

Yea, I think it might be better (an opportunity) for us to delete this unused isCustomField column (should be hard), then make this whole solution, store/rest/persistence that bit less complicated. And if not for anything else, just to avoid having this discussion again in the future when someone else will try and touch this logic.

WILL DO 🏃 💨💨💨

…to migrate/wc-new-visitor-stats-model-to-room

# Conflicts:
#	libs/fluxc-plugin/schemas/org.wordpress.android.fluxc.persistence.WCAndroidDatabase/64.json
Due to 146ebde a merge conflict
appeared which got solved via 781b01b,
with the main '64.json' file, but also a hidden merge conflict on
'WCAndroidDatabase', which includes:

- const val WC_DATABASE_VERSION = 65
- AutoMigration(from = 64, to = 65),

I accepted the current version of '64.json' and created a new one, the
'65.json' because the database version for this exact change has now
been updated to '65' (from '64').
@ParaskP7
Copy link
Contributor Author

WILL DO 🏃 💨💨💨

FYI: This is now done @hichamboushaba (cc @wzieba): e1694cd (mainly)

Please take another look and let me know if everything looks and works as expected, thank you much! 🙏

PS: No rush! 🆗

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Member

@hichamboushaba hichamboushaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @ParaskP7, thanks so much for the code cleanup

@ParaskP7
Copy link
Contributor Author

Nice work @ParaskP7, thanks so much for the code cleanup

Awesome @hichamboushaba , thanks so much for yet another code review/testing session on that! 🙇 ❤️ 🚀

FYI: I'll merge this first thing on Monday.

@ParaskP7 ParaskP7 merged commit 603ef16 into trunk Sep 15, 2025
17 checks passed
@ParaskP7 ParaskP7 deleted the migrate/wc-new-visitor-stats-model-to-room branch September 15, 2025 07:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: technical debt Represents or solves tech debt of the project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants